#!/bin/bash

# List of kernels that are maintained upstream
_current_kernels=("6.18" "6.17" "6.12" "6.6" "6.1" "5.15" "5.10" "5.4")

# List of kernels that are no longer maintained either upstream or locally
_eol_kernels=("6.16" "6.15" "6.14" "6.13" "6.11" "6.10" "6.9" "6.8" "6.7" "6.5" "6.4" "6.3" "6.2" "6.0" "5.19" "5.18" "5.17" "5.16" "5.14" "5.13" "5.12" "5.11" "5.9" "5.8" "5.7")

_deprecated_config_vars=(
  "_use_tmpfs"
  "_source_in_tmpfs"
  "_tmpfs_path"
  "_cacule_rdb"
  "_cacule_rdb_interval"
  "_tt_high_hz"
  "_x86_64_isalvl"
  "_dracut_options"
  "_bcachefs"
  "_eevdf_sched_ext_support"
  "_debugdisable_disabled_entry"
  "_debugdisable_enabled_entry"
)

typeset -Ag _all_scheds
_all_scheds=(
  ["pds"]="Project C's Priority and Deadline based Skiplist multiple queue scheduler (PDS)"
  ["bmq"]="Project C's BitMap Queue Scheduler (BMQ)"
  ["eevdf"]="Earliest Eligible Virtual Deadline First scheduler (EEVDF). Linux kernel's default for ≥ 6.6"
  ["cfs"]="Completely Fair Scheduler (CFS). Linux kernel's default for ≤ 6.5"
  ["muqss"]="Multiple Queue Skiplist Scheduler (MuQSS)"
  ["upds"]="TkG's Undead PDS"
  ["bore"]="Burst-Oriented Response Enhancer Scheduler (BORE)"
)

typeset -Ag _cpu_marchs
_cpu_marchs=(
  ["x86-64"]="Baseline for any X86 CPU (default)"
  ["x86-64-v2"]="Baseline for X86 CPUs newer than ~2008 (see https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels)"
  ["x86-64-v3"]="Baseline for X86 CPUS newer than ~2013 (see link above)"
  ["x86-64-v4"]="Baseline for Intel Skylake or newer, AMD zen4 or newer (see link above)"
  ["native"]="Automatically tune for the current CPU"
  ["znver5"]="AMD Ryzen 9000 | Mobile Ryzen AI (MAX) 300 | EPYC 9005"
  ["znver4"]="AMD Ryzen 8000/7000 | Mobile Ryzen 200 | Threadripper 7000WX | EPYC 9004/8004/4004"
  ["znver3"]="AMD Ryzen 6000/5000 | Mobile Ryzen 7030/5000 | Threadripper 5000WX | EPYC 7003"
  ["znver2"]="AMD Ryzen 4000/3000 | Mobile Ryzen 5[3,5,7]00U | Threadripper 3000WX | EPYC 7002"
  ["znver1"]="AMD Ryzen 1000/2000 | Athlon 200/300/3000 | Threadripper 1000 | EPYC 7001"
  ["arrowlake-s"]="Intel Desktop Core Ultra 200"
  ["arrowlake"]="Intel Laptop Core Ultra 200"
  ["lunarlake"]="Intel Laptop Core Ultra 200V"
  ["meteorlake"]="Intel Laptop Core Ultra 100"
  ["raptorlake"]="Intel Core 14th & 13th gen"
  ["alderlake"]="Intel Core 12th gen"
  ["rocketlake"]="Intel Core 11th gen (see https://en.wikipedia.org/wiki/Rocket_Lake)"
  ["tigerlake"]="Intel Laptop Core 11th gen"
  ["icelake-client"]="Intel Desktop Core 10th gen"
  ["skylake"]="Intel Desktop Core 6th to 9th gen"
)

typeset -Ag _kernel_git_remotes
_kernel_git_remotes=(
  ["kernel.org"]="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git"
  ["googlesource.com"]="https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable"
  ["gregkh"]="https://github.com/gregkh/linux.git"
  ["torvalds"]="https://github.com/torvalds/linux.git"
)

# PREEMPT_RT's supported kernel subversion
typeset -Ag _rt_subver_map
_rt_subver_map=(
  ["5.4"]="271"
  ["5.9"]="1"
  ["5.10"]="221"
  ["5.11"]="4"
  ["5.14"]="2"
  ["5.15"]="160"
  ["5.16"]="2"
  ["5.17"]="1"
  ["6.0"]="5"
  ["6.1"]="99"
  ["6.3"]="3"
  ["6.4"]="6"
  ["6.5"]="2"
  ["6.6"]="41"
  ["6.7"]="0"
  ["6.8"]="2"
  ["6.9"]="0"
  ["6.10"]="rc6"
)

# PREEMPT_RT's patch revision for the kernel
# We separated this to allow for forcing the application of the patch when _preempt_rt_force=1 on version mismatch
typeset -Ag _rt_rev_map
_rt_rev_map=(
  ["5.4"]="89"
  ["5.9"]="20"
  ["5.10"]="113"
  ["5.11"]="11"
  ["5.14"]="21"
  ["5.15"]="77"
  ["5.16"]="19"
  ["5.17"]="17"
  ["6.0"]="14"
  ["6.1"]="36"
  ["6.3"]="15"
  ["6.4"]="8"
  ["6.5"]="8"
  ["6.6"]="37"
  ["6.7"]="6"
  ["6.8"]="11"
  ["6.9"]="5"
  ["6.10"]="11"
)

_undefine() {
  for _config_name in "$@"; do
    scripts/config -k --undefine "${_config_name}"
    echo "Undefined ${_config_name}" >> "$_where"/logs/prepare.log.txt
  done
}

_enable() {
  for _config_name in "$@"; do
    scripts/config -k --enable "${_config_name}"
    echo "Enabled ${_config_name}" >> "$_where"/logs/prepare.log.txt
  done
}

_disable() {
  for _config_name in "$@"; do
    scripts/config -k --disable "${_config_name}"
    echo "Disabled ${_config_name}" >> "$_where"/logs/prepare.log.txt
  done
}

_module() {
  for _config_name in "$@"; do
    scripts/config -k --module "${_config_name}"
    echo "Defined ${_config_name} as module" >> "$_where"/logs/prepare.log.txt
  done
}

_prompt_from_array() {
  # Prompts from array, selects default index on empty user input
  # Set the _default_index variable to enable default index selection

  if [ $# = "0" ]; then
    warning "Prompting on an empty array, please report this issue."
    exit 1
  fi

  unset _selected_index

  local _N=$(($#-1))
  local _index=0
  for _value in "$@"; do
    if [ "$_index" = "$_default_index" ]; then
      plain "  > ${_index}) ${_value}"
    else
      plain "    ${_index}) ${_value}"
    fi
    _index=$(($_index + 1))
  done
  while true; do
    read -rp "[0-${_N}]: ";
    if [[ -z "$REPLY" && -n "$_default_index" ]]; then
      _selected_index="$_default_index"
      break
    elif [[ "$REPLY" =~ ^[0-9]+$ && 0 -le "$REPLY" && "$REPLY" -le $_N ]]; then
      _selected_index="$REPLY"
      break
    else
      echo "Wrong selection: select any number in 0-$_N"
    fi
  done

  local _natural_index=$(($_selected_index + 1))
  _selected_value="${!_natural_index}"
  plain "Selected: ${_selected_value}"
}

_prompt_from_dict() {
  # Prompts from dict that it receives by name, selects default key on empty user input
  # Set the _default_key variable to enable default index selection

  if [ $# != "1" ]; then
    warning "Prompting on an empty dict, please report this issue."
    exit 1
  fi

  unset _selected_key

  declare -n _dict="$1"

  for _key in "${!_dict[@]}"; do
    if [ "$_key" = "$_default_key" ]; then
      plain "  > ${_key}: ${_dict[$_key]}"
    else
      plain "    ${_key}: ${_dict[$_key]}"
    fi
  done
  while true; do
    read -rp "Section: ";
    if [[ -z "$REPLY" && -n "$_default_key" ]]; then
      _selected_key="$_default_key"
      break
    elif [[ -v _dict["$REPLY"] ]]; then
      _selected_key="$REPLY"
      break
    else
      echo "Selection not part of the possibilities, please re-try"
    fi
  done

  plain "Selected: ${_selected_key}"
}

_set_kver_internal_vars() {
  # Sets _basever, _basekernel and _sub global variables by reading Makefile at the root of the kernel sources

  # VERSION, PATCHLEVEL, SUBLEVEL and EXTRAVERSION are defined in the first lines of kernel-src/Makefile
  # we source those lines to retrieve those strings
  local VERSION=`cat "$_kernel_work_folder_abs"/Makefile | head -n 10 | grep ^VERSION | awk '{print $3}'`
  local PATCHLEVEL=`cat "$_kernel_work_folder_abs"/Makefile | head -n 10 | grep ^PATCHLEVEL | awk '{print $3}'`
  local SUBLEVEL=`cat "$_kernel_work_folder_abs"/Makefile | head -n 10 | grep ^SUBLEVEL | awk '{print $3}'`
  local EXTRAVERSION=`cat "$_kernel_work_folder_abs"/Makefile | head -n 10 | grep ^EXTRAVERSION | awk '{print $3}'`

  # examples: "6.0", "5.15", "5.4"
  _basekernel="$VERSION.$PATCHLEVEL"

  # examples: "60", "515", "54"
  _basever="$VERSION$PATCHLEVEL"

  # examples: "5", "rc2", "122"
  if [ -n "$EXTRAVERSION" ]; then
    [[ ! "$EXTRAVERSION" == "-rc"* ]] && error "$EXTRAVERSION does not start with '-rc'" && exit 1
    _sub="${EXTRAVERSION:1}"
  else
    _sub="$SUBLEVEL"
  fi

  msg2 "kernel version to build is $VERSION.$PATCHLEVEL.$_sub"

  # Append a zero to the minor if it has single digit
  [[ ${#PATCHLEVEL} == "1" ]] && PATCHLEVEL="0$PATCHLEVEL"

  # examples: "600", "515", "504" we use this variable to have proper comparisons
  _kver="$VERSION$PATCHLEVEL"

  if [ $_kver -le 605 ]; then
    # chosen kernel <= 6.5
    _default_cpu_sched="cfs"
  else
    # chosen kernel >= 6.6
    _default_cpu_sched="eevdf"
  fi

  echo -e "_basekernel='$_basekernel'\n_basever='$_basever'\n_sub='$_sub'\n_kver='$_kver'\n_default_cpu_sched='$_default_cpu_sched'" >> "$_where"/BIG_UGLY_FROGMINER
}

_set_kernel_version() {

  # if $_version is a valid x.y version, define $_kernel_git_tag with latest x.y.z from $_kver_latest_tags_map
  # if $_version is a valid x.y.z version, define $_kernel_git_tag directly
  # Otherwise, empty it so it gets prompted

  _define_kernel_tags

  if [[ "$_version" == *-latest ]]; then
    local kernel_ver="${_version::-7}"
    if [[ -v _kver_latest_tags_map["$kernel_ver"] ]]; then
      msg2 "Checking out latest kernel version of $kernel_ver"
      _kernel_git_tag="${_kver_latest_tags_map[$kernel_ver]}"
    else
      error "non-existing kernel version associated with $_version"
      exit 1
    fi
  elif [[ -n "$_version" ]]; then
    if echo "$_kernel_tags" | grep -E "^$_version$" &> /dev/null; then
      _kernel_git_tag="$_version"
    else
      error "tag \"$_version\" not found in remote git repository"
      exit 1
    fi
  else
    msg2 "Which kernel version do you want to install?"

    # Create a list of currently maintained kernels versions with their sub-version added

    _kernel_fullver_list=()
    for _key in "${_current_kernels[@]}"; do
      if [[ -v _kver_latest_tags_map["$_key"] ]]; then
        _kernel_fullver_list+=("${_kver_latest_tags_map[$_key]}")
      fi
    done

    _eol_kernel_fullver_list=()
    for _key in "${_eol_kernels[@]}"; do
      if [[ -v _kver_latest_tags_map["$_key"] ]]; then
        _eol_kernel_fullver_list+=("${_kver_latest_tags_map[$_key]}")
      fi
    done

    # Add the "Another" entry to enable building unmaintained kernel versions
    if [[ "${#_eol_kernel_fullver_list[@]}" != 0 ]]; then
      _kernel_fullver_list+=("Another (no longer maintained upstream)")
    fi

    # Default index corresponds to latest stable kernel
    # put default index to "1" when the most recent kernel is an rc one
    _default_index="0"
    if [[ "${_kver_latest_tags_map[${_current_kernels[0]}]}" == *rc* ]]; then
      _default_index="1"
    fi

    _prompt_from_array  "${_kernel_fullver_list[@]}"

    if [[ "${_selected_value}" == Another* ]]; then
      msg2 "Which EOL kernel version do you want to install?"

      # Default index corresponds to latest unmaintained kernel
      _default_index="0"
      _prompt_from_array "${_eol_kernel_fullver_list[@]}"
    fi

    _kernel_git_tag="$_selected_value"
  fi
  echo -e "_kernel_git_tag='$_kernel_git_tag'" >> "$_where"/BIG_UGLY_FROGMINER
}

_set_cpu_scheduler() {

  [[ -z "$_kver" ]] && error "bug: _kver variable not defined but needed" && exit 1

  _avail_cpu_scheds=("$_default_cpu_sched")
  _recommended_sched="$_default_cpu_sched"
  if [[ "$_kver" =~ ^5(04|07|12|13|14|15|16|17)$ ]]; then
    _recommended_sched="pds"
  elif [[ "$_kver" =~ ^5(09|10|11)$ ]]; then
    _recommended_sched="upds"
  fi

  # automatically append "pds" & "bmq" when the patch 0009-prjc.patch exists for the chosen kernel version
  if [[ -f "$_where/linux-tkg-patches/${_basekernel}/0009-prjc.patch" ]]; then
    _avail_cpu_scheds+=("pds" "bmq")
  fi

  # automatically append "bore" when the patch 0002-bore.patch exists for the chosen kernel version
  if [[ -f "$_where/linux-tkg-patches/${_basekernel}/0001-bore.patch" ]]; then
    _avail_cpu_scheds+=("bore")
  fi

  # Manual scheduler additions
  if [ "$_kver" = "504" ]; then
    _avail_cpu_scheds+=("pds" "bmq" "muqss") # PDS is not PrjC in this version, needs to be added manually
  elif [ "$_kver" = "507" ]; then
    _avail_cpu_scheds+=("muqss")
  elif [ "$_kver" = "508" ]; then
    _avail_cpu_scheds+=("upds")
  elif [[ "$_kver" =~ ^(509|511)$ ]]; then
    _avail_cpu_scheds+=("upds" "muqss")
  elif [ "$_kver" = "510" ]; then
    _avail_cpu_scheds+=("upds" "muqss")
  elif [ "$_kver" = "512" ]; then
    _avail_cpu_scheds+=("muqss")
  elif [ "$_kver" = "513" ]; then
    _avail_cpu_scheds+=("muqss")
  fi

  if [ "${_preempt_rt}" = "1" ]; then
    warning "! Since you have enabled _preempt_rt, incompatible cpu schedulers will not be available !"
    _avail_cpu_scheds=("$_default_cpu_sched")
  fi

  # remove unavailable scheds from _all_scheds
  for _sched in "${!_all_scheds[@]}"; do
    if ! [[ "${_avail_cpu_scheds[*]}" =~ "$_sched" ]]; then
      unset _all_scheds["$_sched"]
    fi
  done

  if ! [[ ${_avail_cpu_scheds[*]} =~ "$_cpusched" ]]; then
    warning "Your cpusched selection ( $_cpusched ) is not available for the selected kernel version."
    if [ "$_nofallback" = "true" ]; then
      warning "Since _nofallback is enabled, let's exit..."
      exit 1
    else
      warning "Please select another"
      _cpusched=""
    fi
  fi

  if [ -z "$_cpusched" ]; then
    msg2 "Which CPU sched variant do you want to build/install?"
    msg2 "  If unsure, press enter to use \"$_recommended_sched\" (recommended)"

    _default_key="$_recommended_sched"
    _prompt_from_dict "_all_scheds"
    _cpusched="${_selected_key}"
  fi

  msg2 "Using $_cpusched CPU schduler"

  echo -e "_cpusched='$_cpusched'" >> "$_where"/BIG_UGLY_FROGMINER
}

_set_compiler(){
  if ! [[ "$_compiler" =~ ^(gcc|llvm)$ ]]; then
    if [ -n "$_compiler" ] && [ "$_nofallback" = "true" ]; then
      msg2 "Compiler \" $_compiler \" not recognized, exiting... "
      exit 1
    else
      plain "Which compiler do you want to use?"
      _compiler_array_text=("GCC (recommended)" "Clang/LLVM")
      _compiler_array=("gcc" "llvm")

      _default_index="0"
      _prompt_from_array "${_compiler_array_text[@]}"
      _compiler="${_compiler_array[$_selected_index]}"
    fi
  fi

  if [ "$_compiler" = "llvm" ]; then
    _compiler_name="-llvm"
    llvm_opt="LLVM=1 LLVM_IAS=${_llvm_ias:-0}"
    if [ $_kver -ge 512 ]; then
      if [[ -z "$_lto_mode" || ! "$_lto_mode" =~ ^(no|thin|full)$ ]]; then
        plain "Would you like to enable Clang Link Time Optimizations (LTO) ? It may improve the performance of the kernel."
        warning "This is currently experimental and might result in an unbootable kernel - Not recommended"
        _lto_prompt_array=(
          "No: do not enable LTO"
          "Full: uses 1 thread for Linking, slow and uses more memory, theoretically with the highest performance gains."
          "Thin: uses multiple threads, faster and uses less memory, may have a lower runtime performance than Full."
        )
        _lto_mode_array=("no" "full" "thin")

        _default_index="0"
        _prompt_from_array "${_lto_prompt_array[@]}"
        _lto_mode="${_lto_mode_array[$_selected_index]}"
      fi
      if [ "$_lto_mode" != "no" ]; then
        llvm_opt="LLVM=1 LLVM_IAS=1"
      fi
    elif [[ -n "$_lto_mode" && "$_lto_mode" != "no" ]]; then
      warning "Clang LTO is only available on linux 5.12 onwards."
      _lto_mode="no"
    fi
  else # GCC
    _compiler_name=""
    llvm_opt=""
  fi

  echo -e "_compiler_name='$_compiler_name'\nllvm_opt='$llvm_opt'" >> "$_where"/BIG_UGLY_FROGMINER
}

_define_kernel_abs_paths() {

  _kernel_work_folder_abs="$_where/$_kernel_work_folder/linux-src-git"
  if [[ "$_kernel_work_folder" == /* ]]; then
    _kernel_work_folder_abs="$_kernel_work_folder/linux-tkg/linux-src-git"
  fi
  echo -e "_kernel_work_folder_abs=\"$_kernel_work_folder_abs\"" >> "$_where"/BIG_UGLY_FROGMINER

  _kernel_source_folder_abs="$_where/$_kernel_source_folder/linux-kernel.git"
  if [[ "$_kernel_source_folder" == /* ]]; then
    _kernel_source_folder_abs="$_kernel_source_folder/linux-tkg/linux-kernel.git"
  fi
  echo -e "_kernel_source_folder_abs=\"$_kernel_source_folder_abs\"" >> "$_where"/BIG_UGLY_FROGMINER

}


_define_kernel_tags() {

  _define_kernel_abs_paths

  _git_remote_names=( "${!_kernel_git_remotes[@]}" )

  if [[ ! "$_offline" =~ ^(y|Y|yes|true|True)$ ]]; then
    # fill with kernel.org link if empty
    if [[ -z "$_git_mirror" ]]; then
      msg2 "using default kernel.org git remote"
      _git_mirror="${_kernel_git_remotes['kernel.org']}"
    fi

    # if alias, get actual link from map _kernel_git_remotes
    if [[ "${_git_remote_names[@]}" =~ "$_git_mirror" ]]; then
      msg2 "using git mirror alias $_git_mirror"
      _git_mirror="${_kernel_git_remotes[$_git_mirror]}"
    fi

    if $( ! timeout 10 git ls-remote $_git_mirror >/dev/null ) || \
      [[ "$( git ls-remote $_git_mirror | head -n 1 )" = *502* ]]; then
      error "Remote unreachable or took too long to respond"
      exit 1
    fi

  else

    _git_mirror="$_kernel_source_folder_abs"

  fi

  msg2 "Current git mirror: $_git_mirror"

  # Fill out the latest tags map/dictionary
  if [[ ! -v _kernel_tags ]]; then
    msg2 "Fetching latest kernel tags"
    typeset -g _kernel_tags=$(git -c 'versionsort.suffix=-' \
      ls-remote --exit-code --refs --sort='version:refname' --tags $_git_mirror '*' \
      | cut --delimiter='/' --fields=3)
  fi

  if [[ ! -v _kver_latest_tags_map ]]; then
    typeset -Ag _kver_latest_tags_map
    for _key in "${_current_kernels[@]}" "${_eol_kernels[@]}"; do
      local _tag=$(echo "$_kernel_tags" | grep -F "v$_key"| grep -v "v$_key"[0-9] | tail -1 | cut -c1-)
      if [[ -n "$_tag" ]]; then
        _kver_latest_tags_map[$_key]="$_tag"
      fi
    done
  fi
}


_setup_kernel_work_folder() {

  _define_kernel_abs_paths

  if [ -z "$_kernel_git_tag" ]; then
    warning "internal error: kernel version should be chosen before cloning kernel sources"
    exit 1
  fi

  cd "$_where"

  if ! [ -d "$_kernel_source_folder_abs" ]; then
    msg2 "First initialization of the linux source code git folder"
    mkdir -p "$_kernel_source_folder_abs"
    cd "$_kernel_source_folder_abs"
    git -c init.defaultBranch=master init --bare
  fi

  cd "$_kernel_source_folder_abs"

  if [[ ! "$_offline" =~ ^(y|Y|yes|true|True)$ ]]; then
    msg2 "Fetching tag: $_kernel_git_tag from remote $_git_mirror" | tee "$_where/git_work.log.txt"
    git fetch --depth 1 $_git_mirror tag "$_kernel_git_tag" &>> "$_where/git_work.log.txt"
  fi

  msg2 "Checking out tag: $_kernel_git_tag" | tee -a "$_where/git_work.log"
  msg2 "   in the work folder: $_kernel_work_folder_abs" | tee -a "$_where/git_work.log.txt"

  # The space ' ' in grep -w "$_kernel_work_folder_abs " is important
  # to not match an existing folder with a longer name with the same prefix name
  if [ -d "$_kernel_work_folder_abs" ] && \
     ( git worktree list | grep -w "$_kernel_work_folder_abs " &> /dev/null ) && \
     ( cd "$_kernel_work_folder_abs" && git status &> /dev/null ); then
    # Worktree folder exists and is a valid worktree
    cd "$_kernel_work_folder_abs"
    git reset --hard &>> "$_where/git_work.log.txt"
    git clean -ffdx &>> "$_where/git_work.log.txt"
    git am --abort &>> "$_where/git_work.log.txt" || true
    git rebase --abort &>> "$_where/git_work.log.txt" || true
    git checkout "$_kernel_git_tag" &>> "$_where/git_work.log.txt"
  else
    # In all other cases, just force create the work tree
    rm -rf "$_kernel_work_folder_abs" &>> "$_where/git_work.log.txt"
    git worktree add -f "$_kernel_work_folder_abs" "$_kernel_git_tag" &>> "$_where/git_work.log.txt"
  fi

  _set_kver_internal_vars
}


_tkg_initscript() {

  if ! whereis git > /dev/null 2>&1; then
    warning "the 'git' command is not installed. Please install it then re-run the install script of linux-tkg."
    exit 1
  fi

  if ! git status > /dev/null 2>&1; then
    warning "linux-tkg needs to be git cloned"
    msg2 "please delete the current linux-tkg folder and re-created it with"
    msg2 "git clone --depth=1 https://github.com/Frogging-Family/linux-tkg.git"
    exit 1
  fi

  for _deprecated_var in "${_deprecated_config_vars[@]}"; do
    if [ -n "${!_deprecated_var}" ]; then
      warning "The deprecated config var $_deprecated_var has been set"
      warning "Please check the latest customization.cfg file to see what replaces it"
      exit 1
    fi
  done

  # Default to Arch
  if [ -z "$_distro" ] || [ "$_ispkgbuild" = "true" ]; then
    msg2 "Defaulting to Archlinux target\n"
    _distro="Arch"
  fi

  # create build dir early
  _path="${_where}"

  # Clean the logs folder
  [ -e "${_where}/logs" ] && rm -rf "${_where}/logs"
  mkdir -p "${_where}/logs"

  _set_kernel_version

  _setup_kernel_work_folder

  _set_cpu_scheduler

  cp "$_where"/linux-tkg-patches/${_basekernel}/*.patch "$_where" # copy patches inside the PKGBUILD's dir to preserve makepkg sourcing and md5sum checking
  cp "$_where"/linux-tkg-config/${_basekernel}/* "$_where" # copy config files and hooks inside the PKGBUILD's dir to preserve makepkg sourcing and md5sum checking

  _set_compiler

  if [ -n "$_custom_pkgbase" ]; then
    echo -e "_custom_pkgbase=\"$_custom_pkgbase\"" >> "$_where"/BIG_UGLY_FROGMINER
  fi
}

user_patcher() {
    for _f in "$_where"/*."${_userpatch_ext}patch" "$_where"/*."${_userpatch_ext}revert"; do
      if [ -e "${_f}" ]; then
        warning "Found userpatch file ${f##*/} in the PKGBUILD directory." #"
        warning "Userpatches must now be placed in version-specific subdirectories (linux${_basever}-tkg-userpatches for this kernel version)."
        warning "The patch will not be applied."
      fi
    done

    echo -e "linux-tkg user patches, if any:\n" >> "$_where"/logs/prepare.log.txt

	# To patch the user because all your base are belong to us
	local _patches=("$_where"/linux"$_basever"-tkg-userpatches/*."${_userpatch_ext}revert")
	if [ ${#_patches[@]} -ge 2 ] || [ -e "${_patches}" ]; then
	  if [ "$_user_patches_no_confirm" != "true" ]; then
	    msg2 "Found ${#_patches[@]} 'to revert' userpatches for ${_userpatch_target}:"
	    printf '%s\n' "${_patches[@]}"
	    read -rp "Do you want to install it/them? - Be careful with that ;)"$'\n> N/y : ' _CONDITION;
	  fi
	  if [[ "$_CONDITION" =~ [yY] ]] || [ "$_user_patches_no_confirm" = "true" ]; then
	    for _f in "${_patches[@]}"; do
	      if [ -e "${_f}" ]; then
	        msg2 "######################################################"
	        msg2 ""
	        msg2 "Reverting your own ${_userpatch_target} patch ${_f}"
	        msg2 ""
	        msg2 "######################################################"
	        patch -Np1 -R < "${_f}"
	        echo "Reverted your own patch ${_f}" >> "$_where"/logs/prepare.log.txt
	      fi
	    done
	  fi
	fi

	_patches=("$_where"/linux"$_basever"-tkg-userpatches/*."${_userpatch_ext}patch")
	if [ ${#_patches[@]} -ge 2 ] || [ -e "${_patches}" ]; then
	  if [ "$_user_patches_no_confirm" != "true" ]; then
	    msg2 "Found ${#_patches[@]} userpatches for ${_userpatch_target}:"
	    printf '%s\n' "${_patches[@]}"
	    read -rp "Do you want to install it/them? - Be careful with that ;)"$'\n> N/y : ' _CONDITION;
	  fi
	  if [[ "$_CONDITION" =~ [yY] ]] || [ "$_user_patches_no_confirm" = "true" ]; then
	    for _f in "${_patches[@]}"; do
	      if [ -e "${_f}" ]; then
	        msg2 "######################################################"
	        msg2 ""
	        msg2 "Applying your own ${_userpatch_target} patch ${_f}"
	        msg2 ""
	        msg2 "######################################################"
	        patch -Np1 < "${_f}"
	        echo "Applied your own patch ${_f}" >> "$_where"/logs/prepare.log.txt
	      fi
	    done
	  fi
	fi
}

_tkg_patcher() {
  if [ -e "$tkgpatch" ] && [[ $(wc -l <"$tkgpatch") -ge 7 ]]; then
    msg2 "$_msg"
    echo -e "### Applying ${tkgpatch##*/}... ###" >> "$_where"/logs/prepare.log.txt
    patch -Np1 -i "$tkgpatch" >> "$_where"/logs/prepare.log.txt || error "An error was encountered applying patches. It was logged to the prepare.log.txt file."
    echo -e "\n" >> "$_where"/logs/prepare.log.txt
  else
    msg2 "Skipping patch ${tkgpatch##*/}...\n         (unavailable for this kernel version)"
  fi
}

_tkg_srcprep() {

  cd "$_kernel_work_folder_abs"

  if (( "$_kver" <= 602 )); then
    msg2 "Setting version..."
    scripts/setlocalversion --save-scmversion
  fi

  if [ "${_distro}" = "Arch" ]; then
    if [ -n "$_custom_pkgbase" ]; then
      if [[ "$_custom_pkgbase" != *tkg* ]]; then
        _localversion_tag="-tkg-"
      else
        _localversion_tag="-"
      fi
      echo "-${pkgrel}${_localversion_tag}${_custom_pkgbase}" > localversion.10-pkgrel
      echo -e "Version tail set to \"-${pkgrel}${_localversion_tag}${_custom_pkgbase}\"\n" > "$_where"/logs/prepare.log.txt
    else
      echo "-$pkgrel-tkg-${_cpusched}${_compiler_name}" > localversion.10-pkgrel
      echo -e "Version tail set to \"-$pkgrel-tkg-${_cpusched}${_compiler_name}\"\n" > "$_where"/logs/prepare.log.txt
    fi
    echo "" > localversion.20-pkgname
  fi

  # Hardened Patches
  if [ "${_configfile}" = "config_hardened.x86_64" ] && [ "${_cpusched}" = "cfs" ]; then
    tkgpatch="$srcdir/0012-linux-hardened.patch"
    _msg="Using linux hardened patchset" && _tkg_patcher
  else
    tkgpatch="$srcdir/0001-add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by.patch"
    _msg="Using Arch patches" && _tkg_patcher
  fi

  if [ -z $_debug ]; then

  # PREEMPT_RT patch
  if [ "${_preempt_rt}" = "1" ] && [[ $_kver -lt 612 ]]; then
    if [ "${_rt_subver_map[$_basekernel]+_}" = "_" ]; then
      preempt_rt_ksubver="${_rt_subver_map[$_basekernel]}"
      # Check if subversion is supported, skip check if forced
      if [ "${_preempt_rt_force}" = "1" ] || [ "${preempt_rt_ksubver}" = "${_sub}" ]; then
        if [[ "${_sub}" == *rc* ]]; then
          _separator="-"
        else
          _separator="."
        fi
        if [[ "${preempt_rt_ksubver}" == 0 ]]; then
          _separator=
          preempt_rt_ksubver=
        fi
        preempt_rt_file_gz="patch-${_basekernel}${_separator}${preempt_rt_ksubver}-rt${_rt_rev_map["$_basekernel"]}.patch.gz"
        preempt_rt_file=`basename ${preempt_rt_file_gz} .gz`
        curl "https://cdn.kernel.org/pub/linux/kernel/projects/rt/${_basekernel}/${preempt_rt_file_gz}" > "$srcdir"/"${preempt_rt_file_gz}"
        last_pwd=`pwd`
        cd "$srcdir"
        gunzip -k "$srcdir/${preempt_rt_file_gz}"
        cd "$last_pwd"
        tkgpatch="$srcdir/${preempt_rt_file}"
        _msg="Applying PREEMPT_RT patch" && _tkg_patcher
      else
        warning "Skipping PREEMPT_RT patch for ${_basekernel}.${_sub} (last known good ${_basekernel}.${preempt_rt_ksubver})"
      fi
    else
      warning "Skipping PREEMPT_RT patch on unsupported kernel version"
    fi
  fi

  # TkG
  if [ "$_clear_patches" = "true" ]; then
    tkgpatch="$srcdir/0002-clear-patches.patch"
    _msg="Applying clear linux patches" && _tkg_patcher
  fi

  if [ "$_glitched_base" = "true" ]; then
    tkgpatch="$srcdir/0003-glitched-base.patch"
    _msg="Applying glitched base patch" && _tkg_patcher
  fi

  if [ "${_preempt_rt}" != "1" ]; then
    tkgpatch="$srcdir/0003-glitched-base-nonrt.patch"
    _msg="Applying glitched base non-rt additions patch" && _tkg_patcher
  fi

  tkgpatch="$srcdir/0013-fedora-rpm.patch"
  _msg="RPM: fixing spec generator" && _tkg_patcher

  if [[ "$_STRIP" == "true" ]]; then
    tkgpatch="$srcdir/0013-fedora-strip-modules.patch"
    _msg="RPM: strip modules" && _tkg_patcher
  fi

  if [ "$_distro" = "Suse" ]; then
    tkgpatch="$srcdir/0013-suse-additions.patch"
    _msg="Import Suse-specific patches" && _tkg_patcher
  fi

  if [ "$_distro" = "Gentoo" ]; then
    # https://dev.gentoo.org/~mpagano/genpatches/trunk/
    tkgpatch="$srcdir/0013-gentoo-kconfig.patch"
    _msg="Import Gentoo-kconfig patches" && _tkg_patcher
    tkgpatch="$srcdir/0013-gentoo-print-loaded-firmware.patch"
    _msg="Print firmware names on load" && _tkg_patcher
  fi

  if [ -z $_misc_adds ]; then
    plain "Enable misc additions ? They may contain temporary fixes pending upstream, or some other changes that can break on non-Arch distros."
    read -rp "`echo $'    > [Y]/n : '`" _interactive_misc_adds;
    if [ "$_interactive_misc_adds" != "n" ] && [ "$_interactive_misc_adds" != "N" ]; then
      _misc_adds="true"
    fi
  fi

  if [ "$_misc_adds" = "true" ]; then
    tkgpatch="$srcdir/0012-misc-additions.patch"
    _msg="Applying misc additions patch" && _tkg_patcher
  fi

  _msg="Applying patches for WRITE_WATCH support in Wine"
  tkgpatch="$srcdir/0001-mm-Support-soft-dirty-flag-reset-for-VA-range.patch" && _tkg_patcher
  tkgpatch="$srcdir/0002-mm-Support-soft-dirty-flag-read-with-reset.patch" && _tkg_patcher

  if [ "${_cpusched}" = "muqss" ]; then
    # MuQSS
    _msg="Applying MuQSS base patch"
    tkgpatch="$srcdir/0004-${_basekernel}-ck1.patch" && _tkg_patcher

    if [ "${_aggressive_ondemand}" = "true" ]; then
      _msg="Applying MuQSS agressive ondemand governor patch"
      tkgpatch="$srcdir/0004-glitched-ondemand-muqss.patch" && _tkg_patcher
    fi

    _msg="Applying Glitched MuQSS patch"
    tkgpatch="$srcdir/0004-glitched-muqss.patch" && _tkg_patcher

  elif [ "${_cpusched}" = "upds" ] || [ "${_cpusched}" = "pds" ]; then
    # upds naming quirk
    if [ "${_cpusched}" = "upds" ];then
      # is it dead or alive
      doa="-undead"
    fi

    # PDS-mq
    _msg="Applying PDS base patch"
    if [ "${_cpusched}" = "upds" ] || ( [ "$_kver" = "504" ] || [ "$_kver" = "507" ] && [ "${_cpusched}" = "pds" ] ); then
      tkgpatch="$srcdir/0005-v${_basekernel}_undead-pds099o.patch" && _tkg_patcher
      if [ "${_aggressive_ondemand}" = "true" ]; then
        _msg="Applying PDS agressive ondemand governor patch"
        tkgpatch="$srcdir/0005${doa}-glitched-ondemand-pds.patch" && _tkg_patcher
      fi
    else
      tkgpatch="$srcdir/0009-prjc.patch" && _tkg_patcher
      if [ "${_aggressive_ondemand}" = "true" ]; then
        _msg="Applying prjc PDS/BMQ agressive ondemand governor patch"
        tkgpatch="$srcdir/0009-glitched-ondemand-bmq.patch" && _tkg_patcher
      fi
    fi

    _msg="Applying Glitched PDS patch"
    tkgpatch="$srcdir/0005${doa}-glitched-pds.patch" && _tkg_patcher

  elif [ "${_cpusched}" = "bmq" ]; then
    # Project C / BMQ
    _msg="Applying Project C / BMQ base patch"
    if [ "$_kver" != "504" ]; then
      tkgpatch="$srcdir/0009-prjc.patch" && _tkg_patcher
    else
      tkgpatch="$srcdir/0009-bmq.patch" && _tkg_patcher
    fi

    if [ "${_aggressive_ondemand}" = "true" ] && [ "$_kver" != "504" ]; then
      _msg="Applying BMQ agressive ondemand governor patch"
      tkgpatch="$srcdir/0009-glitched-ondemand-bmq.patch" && _tkg_patcher
    fi

    _msg="Applying Glitched BMQ patch"
    tkgpatch="$srcdir/0009-glitched-bmq.patch" && _tkg_patcher
  elif [ "${_cpusched}" = "bore" ]; then
    _msg="Applying BORE patch"
    tkgpatch="$srcdir/0001-bore.patch" && _tkg_patcher
  elif [ "${_cpusched}" = "cfs" ]; then
    _msg="Applying Glitched CFS additions patch"
    tkgpatch="$srcdir/0003-glitched-cfs-additions.patch" && _tkg_patcher
  elif [[ "${_cpusched}" =~ "eevdf" ]]; then
    if [[ $_kver == "604" || $_kver == "605" ]]; then
      _msg="Applying Earliest Eligible Virtual Deadline First (EEVDF) scheduler patch"
      tkgpatch="$srcdir/0003-eevdf.patch" && _tkg_patcher

      _msg="Applying eevdf-Disable-DELAY_DEQUEUE patch"
      tkgpatch="$srcdir/0004-eevdf-Disable-DELAY_DEQUEUE.patch" && _tkg_patcher

    elif [[ $_kver -ge 606 ]]; then
      _msg="Applying Glitched EEVDF additions patch"
      tkgpatch="$srcdir/0003-glitched-eevdf-additions.patch" && _tkg_patcher
    fi
  fi

  if [ "${_cpusched}" = "cfs" ] || [ "${_cpusched}" = "bore" ] || [[ "${_cpusched}" =~ "eevdf" ]]; then
    _msg="Applying Glitched CFS/EEVDF patch"
    tkgpatch="$srcdir/0003-glitched-cfs.patch" && _tkg_patcher
  fi

  fi

  if [ -z "${_configfile}" ]; then
    msg2 "Using archlinux's default config file for kernel ${_basekernel}"
    cat "${srcdir}"/config.x86_64 > ./.config
  elif [ "${_configfile}" = "config_hardened.x86_64" ]; then
    msg2 "Using archlinux's hardened config file for kernel ${_basekernel}"
    cat "${srcdir}"/config_hardened.x86_64 > ./.config
  elif [ "${_configfile}" = "running-kernel" ]; then
    if [ -f /boot/config-`uname -r` ];then
      msg2 "Using /boot/config-`uname -r` as config file"
      cp /boot/config-`uname -r` .config
    elif [ -f /proc/config.gz ];then
      msg2 "Using /proc/config.gz as config file"
      zcat --verbose /proc/config.gz > .config
    else
      warning "Cannot get config file of running kernel"
      exit 1
    fi
  else
    msg2 "Using user-provided config file in ${_where}/${_configfile}"
    cat "${_where}/${_configfile}" > ./.config
  fi

  if [ "${_distro}" = "Arch" ]; then
    # Reset local version string if ever it's in the .config file
    scripts/config --set-str localversion ""
  else
    _disable "LOCALVERSION_AUTO"
    scripts/config --set-str "DEFAULT_HOSTNAME" "(none)"
  fi

  if [ -z $_debug ]; then

  # Set some -tkg defaults
  _disable "DYNAMIC_FAULT" "DEFAULT_FQ_CODEL" "WERROR"
  _enable "DEFAULT_CAKE" "AMD_PRIVATE_COLOR"
  if [ "$_kver" = "504" ]; then
    _module "TP_SMAPI"
    _enable "RAID6_USE_PREFER_GEN"
  fi
  if [ "$_kver" = "504" ] || [ "$_kver" = "509" ]; then
    scripts/config --set-val "RCU_BOOST_DELAY" "0"
  fi
  _disable "NTP_PPS" "ZSWAP_COMPRESSOR_DEFAULT_LZO" "PROFILE_ALL_BRANCHES"
  _enable "CRYPTO_LZ4" "CRYPTO_LZ4HC" "LZ4_COMPRESS" "LZ4HC_COMPRESS" "X86_AMD_PSTATE" "AMD_PINCTRL"
  _disable "DEBUG_FORCE_FUNCTION_ALIGN_64B" "X86_P6_NOP" "RCU_STRICT_GRACE_PERIOD"
  if [ $_kver -le 605 ]; then
    _enable "ZSWAP_COMPRESSOR_DEFAULT_LZ4"
    scripts/config --set-str "ZSWAP_COMPRESSOR_DEFAULT" "lz4"
  fi
  _enable "CPU_FREQ_DEFAULT_GOV_SCHEDUTIL"
  _disable "CPU_FREQ_DEFAULT_GOV_ONDEMAND" "CPU_FREQ_DEFAULT_GOV_CONSERVATIVE" "CPU_FREQ_DEFAULT_GOV_PERFORMANCE" "CPU_FREQ_DEFAULT_GOV_PERFORMANCE_NODEF"
  _module "BLK_DEV_LOOP"

  # Arch tends to set voluntary preemption when moving kernels to LTS, which we don't want as default
  _disable "PREEMPT_VOLUNTARY"
  _enable "PREEMPT"
  if [ "${_preempt_rt}" = "1" ]; then
    if [ $_kver -ge 612 ]; then
      _enable "CONFIG_EXPERT" && _expert="true"
    fi
    _enable "PREEMPT_RT"
  fi

  # This leads to all kinds of issues everytime Arch enables it in defconfig. Let's disable it and be happy.
  _disable "SYSFB_SIMPLEFB"

  # buggy project C/PSI interaction workaround
  if [ "${_cpusched}" = "pds" ] || [ "${_cpusched}" = "bmq" ]; then
    _disable "PSI"
    _enable "PSI_DEFAULT_DISABLED"
    # Disable MLX5_CORE on Prjc
    plain "Disable MLX5_CORE for Prjc"
    _disable "MLX5_CORE"
  fi

  if [[ $_kver -ge 612 ]]; then
    _enable "SCHED_CLASS_EXT"
  fi

  if [ -n "$_custom_commandline" ]; then
    _enable "CMDLINE_BOOL"
    _disable "CMDLINE_OVERRIDE"
    scripts/config --set-str "CMDLINE" "${_custom_commandline}"
  fi

  # openrgb
  _module "I2C_NCT6775"

  # ccache fix
  if [ "$_noccache" != "true" ]; then
    if { [ "$_distro" = "Arch" ] && pacman -Qq ccache &> /dev/null; } || { [ "$_distro" = "Ubuntu" ] && dpkg -l ccache > /dev/null; }; then
      _disable "GCC_PLUGINS"
    fi
  fi
  # Clang LTO
  if [ "$_compiler_name" = "-llvm" ] && [ $_kver -ge 512 ]; then
    if [ "$_lto_mode" = "full" ]; then
      _enable LTO_CLANG_FULL
      _disable LTO_CLANG_THIN
      _disable LTO_NONE
    elif [ "$_lto_mode" = "thin" ]; then
      _disable LTO_CLANG_FULL
      _enable LTO_CLANG_THIN
      _disable LTO_NONE
    else
      _disable LTO_CLANG_FULL
      _disable LTO_CLANG_THIN
      _enable LTO_NONE
    fi
  fi

  if [ "$_distro" = "Gentoo" ]; then
    # Default config for Gentoo as defined in kconfig
    _enable "GENTOO_LINUX" "GENTOO_LINUX_UDEV" "GENTOO_LINUX_PORTAGE" "GENTOO_LINUX_INIT_SCRIPT" "GENTOO_PRINT_FIRMWARE_INFO" "GENTOO_LINUX_INIT_SYSTEMD"
    _disable "GENTOO_KERNEL_SELF_PROTECTION"
  fi

  # Prevent Debian and Ubuntu to sign stuff because it breaks stuff
  if [[ "$_distro" = "Debian" || "$_distro" = "Ubuntu" ]]; then
    #Help Debian cert compile problem.
    scripts/config --set-str "SYSTEM_TRUSTED_KEYS" ""
    if [[ $_kver -lt 612 ]]; then # note: 6.12 removed MODULE_COMPRESS_NONE
      #Debian/Ubuntu don't properly support zstd module compression
      _disable "MODULE_COMPRESS_ZSTD"
      _enable "MODULE_COMPRESS_NONE"
    fi
  fi

  # Build kernel without debug symbols
  [[ "$_STRIP" == "true" ]] && _enable "DEBUG_INFO_NONE"

  if [ "$_font_autoselect" != "false" ]; then
    _disable "FONT_TER16x32"
    _enable "FONT_AUTOSELECT"
  fi

  _is_march_supported() {
    if [[ -z "$1" ]]; then
      error "Internal linux-tkg bug: checking empty march string"
      exit 1
    fi

    # native is supported
    [[ "$1" == "native" ]] && return 0

    # x86-64 is supported
    [[ "$1" == "x86-64" ]] && return 0

    # 'generic' is unsupported, use x86-64 instead
    [[ "$1" == "generic" ]] && return 1

    if [[ "$_compiler" == "gcc" ]]; then
      if ! ( LC_ALL=C gcc --target-help | grep -A 2 -m1 'Known valid arguments for -march=' | sed -n '2p' | grep "$1" &> /dev/null ) ; then
        return 1
      fi
    fi
    if [[ "$_compiler" == "llvm" ]]; then
      # "native" doesn't show up in clang's list, but it's supported
      if ! ( clang -mcpu=help 2>&1 | grep "$1" &> /dev/null ) ; then
        return 1
      fi
    fi
    # When Rust comes, soon :tm:
    # if which rustc &> /dev/null; then
    #   if ! ( rustc --print target-cpus | grep "$1" &> /dev/null ) ; then
    #     return 1
    #   fi
    # fi
  }

  if [ -n "$_processor_opt" ] && ! _is_march_supported "$_processor_opt" ; then
    warning "Processor micro architecture '$_processor_opt' doesn't exist or is unsupported by current $_compiler compiler"
    if [ "$_nofallback" = "true" ]; then
      warning "Since _nofallback is enabled, exiting..."
      exit 1
    else
      warning "Please select another"
      _processor_opt=""
    fi
  fi

  if [ -z "$_processor_opt" ]; then

    # Drop march strings that are unsupported by current compiler
    local _unsupported_marchs=""
    for _march in "${!_cpu_marchs[@]}"; do
      if ! _is_march_supported "$_march"; then
        _unsupported_marchs+="$_march "
        unset _cpu_marchs["$_march"]
      fi
    done

    if [[ -n "$_unsupported_marchs" ]]; then
      msg2 "These micro-architectures are unsupported by the currently selected $_compiler compiler"
      msg2 "  $_unsupported_marchs"
    fi

    msg2 "Please select the desired CPU micro-architecture"
    msg2 "  Notes:"
    msg2 "  - To get the full list of microarchitectures supported by your compiler"
    msg2 "    - Clang: clang -mcpu=help"
    msg2 "    - GCC: gcc --target-help | grep -A 2 -m1 'Known valid' | sed -n '2p'"
    msg2 "  - Supported 'march' strings not shown bellow can be set directly in the '_processor_opt' variable in 'customization.cfg'"
    _default_key="x86-64"
    _prompt_from_dict '_cpu_marchs'
    _processor_opt="${_selected_key}"
  fi

  msg2 "Setting cpu micro architecture to $_processor_opt"

  echo "_processor_opt='$_processor_opt'" >> "$_where"/BIG_UGLY_FROGMINER

  # Disable some debugging
  if [ "${_debugdisable}" = "true" ]; then
    _disable ACPI_DEBUG BLK_DEBUG_FS BT_DEBUGFS BT_FEATURE_DEBUG CFG80211_DEBUGFS CIFS_DEBUG CMA_DEBUGFS CRYPTO_DEV_CCP_DEBUGFS DEBUG_KERNEL DEBUG_RODATA_TEST DM_DEBUG DYNAMIC_DEBUG DYNAMIC_DEBUG_CORE IWLWIFI_DEBUG NETFS_DEBUG PM_DEBUG PNP_DEBUG_MESSAGES SHRINKER_DEBUG SND_DEBUG SUNRPC_DEBUG VIRTIO_DEBUG KFENCE SLUB_DEBUG RCU_TRACE SCHED_DEBUG DEBUG_MISC X86_DEBUG_FPU PM_ADVANCED_DEBUG PM_SLEEP_DEBUG DEBUG_WX HAVE_DEBUG_KMEMLEAK DEBUG_MEMORY_INIT SCHED_INFO SCHEDSTATS
    _enable DEBUG_INFO_NONE EXPERT
  fi

  # TCP algorithms
  _tcp_cong_alg_list=("yeah" "bbr" "cubic" "vegas" "westwood" "reno")
  _user_set_tcp_alg="false"

  _enable "TCP_CONG_ADVANCED"

  for _alg in "${_tcp_cong_alg_list[@]}"
  do
    _alg_upper=`echo "$_alg" | tr '[a-z]' '[A-Z]'`
    _enable "TCP_CONG_${_alg_upper}"
    if [ "$_tcp_cong_alg" = "$_alg" ];then
      _user_set_tcp_alg="true"
      _enable "DEFAULT_${_alg_upper}"
      scripts/config --set-str "DEFAULT_TCP_CONG" "${_alg}"
    else
      _disable "DEFAULT_${_alg_upper}"
    fi
  done

  if [ "$_user_set_tcp_alg" = "false" ];then
    _enable "DEFAULT_CUBIC"
    scripts/config --set-str DEFAULT_TCP_CONG "cubic"
  fi

  #######


  if [ "${_cpusched}" = "muqss" ]; then
    # MuQSS default config
    _enable "SCHED_MUQSS"
  elif [ "${_cpusched}" = "pds" ]; then
    # PDS default config
    _enable "SCHED_ALT" "SCHED_PDS"
    _disable "SCHED_BMQ"
  elif [ "${_cpusched}" = "upds" ]; then
    # PDS default config
    _enable "SCHED_PDS"
  elif [ "${_cpusched}" = "bmq" ]; then
    # BMQ default config
    _enable "SCHED_ALT" "SCHED_BMQ"
    _disable "SCHED_PDS"
  elif [[ "${_cpusched}" =~ "bore" ]]; then
    _enable "SCHED_BORE"
    scripts/config --set-val "MIN_BASE_SLICE_NS" "$_bore_min_base_slice_ns"
  fi

  if [[ "${_cpusched}" =~ ^(muqss|pds|bmq|upds)$ ]]; then
    # Disable CFS/EEVDF
    _disable "FAIR_GROUP_SCHED"
    _disable "CFS_BANDWIDTH"

    # for linux59-tkg _sched_yield_type is set to 0
    if [ "$_kver" = "509" ]; then
      _sched_yield_type="0"
    fi
    # sched yield type
    if ! [[ "$_sched_yield_type" =~ ^(0|1|2)$ ]]; then
      plain ""
      plain "CPU sched_yield_type - Choose what sort of yield sched_yield will perform."
      plain ""

      _default_index="0"
      if [ "${_cpusched}" = "muqss" ]; then
        plain "For MuQSS"
        yield_type_array_text=(
          "No yield.\n        Supposedly best option for gaming performance but might lead to stability issues on some (AMD) platforms"
          "Yield only to better priority/deadline tasks.\n        Could lead to stability issues on some (Intel) platforms"
          "Expire timeslice and recalculate deadline."
        )
      elif [[ "${_cpusched}" =~ ^(pds|upds)$ ]]; then
        plain "For (U)PDS"
        yield_type_array_text=(
          "No yield.\n        Recommended option for gaming - \"tkg\" default"
          "Yield only to better priority/deadline tasks.\n        Could lead to stability issues on some (Intel) platforms"
          "Expire timeslice and recalculate deadline."
        )
      else #BMQ
        plain "For BMQ (experimental) - No recommended value yet, so try for yourself x) :"
        yield_type_array_text=(
          "No yield."
          "Deboost and requeue task."
          "Set rq skip task."
        )
      fi

      _prompt_from_array "${yield_type_array_text[@]}"
      _sched_yield_type="${_selected_index}"
    fi
    if [ "${_cpusched}" = "upds" ] || ( [ "$_kver" = "504" ] || [ "$_kver" = "507" ] && [ "${_cpusched}" = "pds" ] ); then
      _sched="pds"
    elif [ "${_cpusched}" = "muqss" ]; then
      _sched="MuQSS"
    elif ( [ "$_kver" != "504" ] && [ "$_cpusched" != "bmq" ] && [ "$_cpusched" != "pds" ] ) || ( [ "$_kver" = "504" ] && [ "$_cpusched" = "bmq" ] ); then
      _sched="${_cpusched}"
    else
      _sched="alt_core"
    fi
    if [ "$_sched_yield_type" = "0" ]; then
      sed -i -e 's/int sched_yield_type __read_mostly = 1;/int sched_yield_type __read_mostly = 0;/' ./kernel/sched/${_sched}.c
    elif [ "$_sched_yield_type" = "1" ]; then
      msg2 "Using default CPU sched yield type (1)"
    elif [ "$_sched_yield_type" = "2" ]; then
      sed -i -e 's/int sched_yield_type __read_mostly = 1;/int sched_yield_type __read_mostly = 2;/' ./kernel/sched/${_sched}.c
    fi
  fi

  # Round Robin interval
  if [[ "${_cpusched}" =~ ^(muqss|pds|bmq|upds)$ && "$_kver" -le 606 ]]; then
    _rr_interval_array=("2" "4" "6" "8")
    _rr_interval_array_text=("2ms" "4ms" "6ms" "8ms")

    typeset -A _rr_interval_default_indexes
    _rr_interval_default_indexes=(
      ["muqss"]="2"
      ["pds"]="1"
      ["upds"]="1"
      ["bmq"]="0"
    )

    _default_index="${_rr_interval_default_indexes[$_cpusched]}"

    #Remove whitespaces from variable
    _rr_interval=`echo ${_rr_interval} | tr -d " "`

    if [ "${_rr_interval}" = "default" ]; then
      _rr_interval="${_rr_interval_array[$_default_index]}"
    elif [[ "$_rr_interval" =~ ^(1|2|3|4)$ ]]; then
      _rr_interval=$(($_rr_interval * 2))
    else
      plain ""
      plain "Round Robin interval is the longest duration two tasks with the same nice level will"
      plain "be delayed for. When CPU time is requested by a task, it receives a time slice equal"
      plain "to the rr_interval in addition to a virtual deadline. When using yield_type 2, a low"
      plain "value can help offset the disadvantages of rescheduling a process that has yielded."
      plain ""

      _prompt_from_array "${_rr_interval_array_text[@]}"
      _rr_interval="${_rr_interval_array[$_selected_index]}"
    fi
    msg2 "Using ${_rr_interval}ms round robin interval"

    if [ "$_kver" != "504" ]; then
      if [ "${_cpusched}" = "muqss" ]; then
        sed -i -e "s/int rr_interval __read_mostly = 6;/int rr_interval __read_mostly = ${_rr_interval};/" ./kernel/sched/"${_sched}".c
      elif [ "${_cpusched}" = "upds" ]; then
        sed -i -e "s/#define SCHED_DEFAULT_RR (4)/#define SCHED_DEFAULT_RR (${_rr_interval})/" ./kernel/sched/pds.c
      elif [ "${_cpusched}" = "bmq" ] || [ "${_cpusched}" = "pds" ]; then
        sed -i -e "s/u64 sched_timeslice_ns __read_mostly = (4 * 1000 * 1000);/u64 sched_timeslice_ns __read_mostly = (${_rr_interval} * 1000 * 1000);/" ./kernel/sched/${_sched}.c
      fi
      if [ "${_cpusched}" = "bmq" ]; then
        scripts/config --set-val "SCHED_TIMESLICE" "2"
      fi
    else
      if [ "${_cpusched}" == "muqss" ]; then
        sed -i -e "s/int rr_interval __read_mostly = 6;/int rr_interval __read_mostly = ${_rr_interval};/" ./kernel/sched/"${_sched}".c
      elif [ "${_cpusched}" == "pds" ]; then
        sed -i -e "s/#define SCHED_DEFAULT_RR (4)/#define SCHED_DEFAULT_RR (${_rr_interval})/" ./kernel/sched/"${_sched}".c
      elif [ "${_cpusched}" == "bmq" ]; then
        scripts/config --set-val "SCHED_TIMESLICE" "${_rr_interval}"
      fi
    fi
  fi

  # zenify
  if [ "$_zenify" = "false" ] || [ "$_glitched_base" = "false" ]; then
    _disable "ZENIFY"
  elif [ "$_zenify" = "true" ] && [ "$_glitched_base" = "true" ]; then
    _enable "ZENIFY"
  fi

  # compiler optimization level
  if [ "$_compileroptlevel" = "1" ]; then
    if [ "$_kver" != "504" ]; then
      _disable "CC_OPTIMIZE_FOR_PERFORMANCE_O3"
    else
      _disable "CC_OPTIMIZE_HARDER"
    fi
  elif [ "$_compileroptlevel" = "2" ]; then
    if [[ $_kver -ge 600 ]]; then
      tkgpatch="$srcdir/0013-optimize_harder_O3.patch"
      _msg="Patching O3 optimization"
      _tkg_patcher
    else
      _disable "CC_OPTIMIZE_FOR_PERFORMANCE"
      if [ "$_kver" != "504" ]; then
        _enable "CC_OPTIMIZE_FOR_PERFORMANCE_O3"
      else
        _enable "CC_OPTIMIZE_HARDER"
      fi
    fi
  elif [ "$_compileroptlevel" = "3" ]; then
    _disable "CC_OPTIMIZE_FOR_PERFORMANCE"
    _enable "CC_OPTIMIZE_FOR_SIZE"
    if [ "$_kver" != "504" ]; then
      _disable "CC_OPTIMIZE_FOR_PERFORMANCE_O3"
    else
      _disable "CC_OPTIMIZE_HARDER"
    fi
  fi

  # irq threading
  if [ "$_irq_threading" = "true" ]; then
    _enable "FORCE_IRQ_THREADING"
  elif [ "$_irq_threading" = "false" ]; then
    _disable "FORCE_IRQ_THREADING"
  fi

  # smt nice
  if [ "$_smt_nice" = "true" ]; then
    _enable "SMT_NICE"
  elif [ "$_smt_nice" = "false" ]; then
    _disable "SMT_NICE"
  fi

  # random trust cpu
  if [ "$_random_trust_cpu" = "true" ]; then
    _enable "RANDOM_TRUST_CPU"
  fi

  # rq sharing
  if [ "${_cpusched}" = "muqss" ]; then
    _disable "RQ_NONE" "RQ_SMT" "RQ_MC" "RQ_MC_LLC" "RQ_SMP" "RQ_ALL"
    if [ "$_runqueue_sharing" = "none" ]; then
      _enable "RQ_NONE"
    elif [ -z "$_runqueue_sharing" ] || [ "$_runqueue_sharing" = "smt" ]; then
      _enable "RQ_SMT"
    elif [ "$_runqueue_sharing" = "mc" ]; then
      _enable "RQ_MC"
    elif [ "$_runqueue_sharing" = "smp" ]; then
      _enable "RQ_SMP"
    elif [ "$_runqueue_sharing" = "all" ]; then
      _enable "RQ_ALL"
    elif [ "$_runqueue_sharing" = "mc-llc" ]; then
      _enable "RQ_MC_LLC"
    fi
  fi

  # timer freq
  _avail_timer_frequencies=("100" "250" "300" "500" "750" "1000")
  _avail_timer_frequencies_text=("100 Hz" "250 Hz" "300 Hz" "500 Hz" "750 Hz" "1000 Hz")

  typeset -A _default_timer_frequencies_index
  _default_timer_frequencies_index=(
    ["pds"]="5"
    ["muqss"]="1"
    ["upds"]="5"
    ["cfs"]="5"
    ["eevdf"]="5"
    ["bmq"]="5"
    ["bore"]="5"
  )

  if [[ -n "$_timer_freq" && ! "${_avail_timer_frequencies[*]}" =~ "$_timer_freq" ]]; then
    warning "The entered timer frequency, ${_timer_freq}Hz, is not available, prompting..."
    _timer_freq=""
  fi

  # Default to 1000Hz if _timer_freq is not set
  if [ -z "$_timer_freq" ]; then
    _default_index="${_default_timer_frequencies_index[$_cpusched]}"
    msg2 "Which kernel interrupt timer frequency would you like to use ?"
    msg2 "Higher Hz means lower latency (but higher overhead / less throughput). So it's a double edged sword"
    msg2 "Pick default (aka press enter with empty input) if unsure"
    _prompt_from_array "${_avail_timer_frequencies_text[@]}"
    _timer_freq="${_avail_timer_frequencies[$_selected_index]}"
  fi

  for _freq in "${_avail_timer_frequencies[@]}"; do
    if [ "$_freq" = "$_timer_freq" ]; then
      _enable "HZ_${_freq}" "HZ_${_freq}_NODEF"
    else
      _disable "HZ_${_freq}" "HZ_${_freq}_NODEF"
    fi
  done

  # Use schedutil instead of ondemand on >32 threads machines
  if [[ $(nproc) -gt 32 ]] && [ "$_default_cpu_gov" = "ondemand" ]; then
    warning "The current default governor selected (ondemand) doesn't perform well on >32 threads. Schedutil will be used instead."
    _default_cpu_gov="schedutil"
  fi

  # default cpu gov
  if [ "$_default_cpu_gov" = "performance" ]; then
    _disable "CPU_FREQ_DEFAULT_GOV_SCHEDUTIL"
    _enable "CPU_FREQ_DEFAULT_GOV_PERFORMANCE" "CPU_FREQ_DEFAULT_GOV_PERFORMANCE_NODEF"
  elif [ "$_default_cpu_gov" = "ondemand" ]; then
    _disable "CPU_FREQ_DEFAULT_GOV_SCHEDUTIL"
    _enable "CPU_FREQ_DEFAULT_GOV_ONDEMAND" "CPU_FREQ_GOV_ONDEMAND"
  fi

  # ftrace
  if [ -z "$_ftracedisable" ]; then
    plain ""
    plain "Disable FUNCTION_TRACER/GRAPH_TRACER? Lowers overhead but limits debugging"
    plain "and analyzing of kernel functions."
    read -rp "`echo $'    > N/y : '`" CONDITION2;
  fi
  if [[ "$CONDITION2" =~ [yY] ]] || [ "$_ftracedisable" = "true" ]; then
    _disable "FUNCTION_TRACER" "FUNCTION_GRAPH_TRACER"
  fi

  # disable numa
  if [ -z "$_numadisable" ]; then
    plain ""
    plain "Disable NUMA? Lowers overhead, but breaks CUDA/NvEnc on Nvidia if disabled."
    plain "https://bbs.archlinux.org/viewtopic.php?id=239174"
    read -rp "`echo $'    > N/y : '`" CONDITION3;
  fi
  if [[ "$CONDITION3" =~ [yY] ]] || [ "$_numadisable" = "true" ]; then
    # disable NUMA since 99.9% of users do not have multiple CPUs but do have multiple cores in one CPU
    _disable "NUMA" "AMD_NUMA" "ACPI_NUMA" "X86_64_ACPI_NUMA" "NODES_SPAN_OTHER_NODES" "NUMA_EMU" "NODES_SHIFT" "NEED_MULTIPLE_NODES" "USE_PERCPU_NUMA_NODE_ID"
  fi

  # tickless
  if [[ -z "$_tickless" || ! "$_tickless" =~ ^(0|1|2)$ ]]; then
  # Set to "1" to use CattaRappa mode (enabling full tickless), "2" for tickless idle only, or "0" for periodic ticks.
    plain "Use CattaRappa mode (Tickless/Dynticks) ?"
    _tickless_array_text=(
      "No, use periodic ticks."
      "Yes, full tickless baby!\n       Full tickless can give higher performances in case you use isolation of CPUs for task, in other cases it behaves as Idle."
      "Just tickless idle plz.\n        Just tickless idle can perform better with some platforms (mostly AMD) or CPU schedulers (mostly MuQSS)."
    )
    _default_index="2"
    _prompt_from_array "${_tickless_array_text[@]}"
    _tickless="${_selected_index}"
  fi
  if [ "$_tickless" = "0" ]; then
    _disable "NO_HZ_FULL_NODEF" "NO_HZ_IDLE" "NO_HZ_FULL" "NO_HZ" "NO_HZ_COMMON" "VIRT_CPU_ACCOUNTING" "VIRT_CPU_ACCOUNTING_GEN"
    _enable "HZ_PERIODIC" "TICK_CPU_ACCOUNTING"
  elif [ "$_tickless" = "1" ]; then
    _disable "HZ_PERIODIC" "NO_HZ_IDLE" "TICK_CPU_ACCOUNTING" "CONTEXT_TRACKING_FORCE"
    _enable "NO_HZ_FULL_NODEF" "NO_HZ_FULL" "NO_HZ" "NO_HZ_COMMON" "CONTEXT_TRACKING" "VIRT_CPU_ACCOUNTING" "VIRT_CPU_ACCOUNTING_GEN"
  else
    _disable "NO_HZ_FULL_NODEF" "HZ_PERIODIC" "NO_HZ_FULL" "TICK_CPU_ACCOUNTING" "CONTEXT_TRACKING_FORCE"
    _enable "NO_HZ_IDLE" "NO_HZ" "NO_HZ_COMMON" "CONTEXT_TRACKING" "VIRT_CPU_ACCOUNTING" "VIRT_CPU_ACCOUNTING_GEN"
  fi

  # acs override
  tkgpatch="$srcdir/0006-add-acs-overrides_iommu.patch"
  if [ -e "$tkgpatch" ]; then
    if [ -z "$_acs_override" ]; then
      plain ""
      plain "Use ACS override patch?"
      plain "https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF#Bypassing_the_IOMMU_groups_.28ACS_override_patch.29"
      read -rp "`echo $'    > N/y : '`" CONDITION7;
    fi
    if [[ "$CONDITION7" =~ [yY] ]] || [ "$_acs_override" = "true" ]; then
      _msg="Patching ACS override"
      _tkg_patcher
    fi
  fi

  # MGLRU
  tkgpatch="$srcdir/0010-lru_${_basekernel}.patch"
  if [ -e "$tkgpatch" ] && [[ ! "$CONDITION8" =~ [yY] ]]; then
    if [ -z "$_mglru" ]; then
       plain ""
       plain "Add multi-generational LRU framework support (improving memory pressure handling)? "
       plain "https://lore.kernel.org/lkml/20220706220022.968789-1-yuzhao@google.com/"
       read -rp "`echo $'    > N/y : '`" CONDITION_mglru;
    fi
    if [[ "$CONDITION_mglru" =~ [yY] ]] || [ "$_mglru" = "true" ]; then
       _msg="Patching MGLRU in"
       _tkg_patcher

       _enable "LRU_GEN" "LRU_GEN_ENABLED"
       _disable "LRU_GEN_STATS"
    fi
  fi

  # fsync (futex_waitv) support
  tkgpatch="$srcdir/0007-v${_basekernel}-futex_waitv.patch"
  if [ -e "$tkgpatch" ]; then
    if [ -z "$_fsync_backport" ]; then
      plain ""
      plain "Enable support for futex_waitv, backported patches for fsync from 5.16 Kernel"
      plain "! Will disable futex2 patchset !"
      plain "https://github.com/andrealmeid/futex_waitv_patches"
      plain "https://github.com/ValveSoftware/wine/pull/128"
      read -rp "`echo $'    > N/y : '`" CONDITION9;
    fi
    if [[ "$CONDITION9" =~ [yY] ]] || [ "$_fsync_backport" = "true" ]; then
      _msg="Patching fsync support"
      _tkg_patcher
      _fsync_futex2="false"
    fi
  else
    _fsync_backport="false"
  fi

  # fsync legacy support
  if [[ $_kver > 515 ]] || [[ "$CONDITION9" =~ [yY] ]] || [ "$_fsync_backport" = "true" ]; then
    tkgpatch="$srcdir/0007-v${_basekernel}-fsync_legacy_via_futex_waitv.patch"
  else
    tkgpatch="$srcdir/0007-v${_basekernel}-fsync_legacy.patch"
  fi
  if [ -e "$tkgpatch" ]; then
    if [ -z "$_fsync_legacy" ]; then
      plain ""
      plain "Enable support for FUTEX_WAIT_MULTIPLE (opcode 31) - fsync legacy used in Valve Proton 4.11, 5.0 and 5.13"
      plain "https://steamcommunity.com/games/221410/announcements/detail/2957094910196249305"
      if [[ "$CONDITION9" =~ [yY] ]] || [ "$_fsync_backport" = "true" ]; then
        plain "Will be used as a fallback to futex_waitv on older Proton builds if enabled"
      fi
      read -rp "`echo $'    > N/y : '`" CONDITION10;
    fi
    if [[ "$CONDITION10" =~ [yY] ]] || [ "$_fsync_legacy" = "true" ]; then
      _msg="Patching fsync legacy support"
      _tkg_patcher

      # futex2 support
      tkgpatch="$srcdir/0007-v${_basekernel}-futex2_interface.patch"
      if [ -e "$tkgpatch" ]; then
        if [ -z "$_fsync_futex2" ]; then
          plain ""
          plain "Enable support for futex2, a DEPRECATED replacement for esync and fsync in Valve Proton 5.13 experimental"
          plain "Can be enabled alongside fsync legacy patchset to have a fallback option"
          plain "https://gitlab.collabora.com/tonyk/linux/-/tree/futex2-dev"
          plain "https://github.com/ValveSoftware/Proton/issues/4568"
          read -rp "`echo $'    > N/y : '`" CONDITION11;
        fi
        if [[ "$CONDITION11" =~ [yY] ]] || [ "$_fsync_futex2" = "true" ]; then
          _msg="Patching futex2 support"
          _tkg_patcher
          _enable "FUTEX2"
          _enable "EXPERT" && _expert="true"
        fi
      fi

    fi
  fi

  if [ "$_expert" = "true" ]; then
    echo -e "\r# start of config expert\r
# CONFIG_BASE_SMALL is not set\r
# CONFIG_DEBUG_RSEQ is not set\r
# CONFIG_PC104 is not set\r
# CONFIG_SLUB_MEMCG_SYSFS_ON is not set\r
# CONFIG_SLOB is not set\r
# CONFIG_PROCESSOR_SELECT is not set\r
# CONFIG_SUSPEND_SKIP_SYNC is not set\r
# CONFIG_DPM_WATCHDOG is not set\r
# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set\r
# CONFIG_PCI_CNB20LE_QUIRK is not set\r
# CONFIG_ISA_BUS is not set\r
CONFIG_KVM_WERROR=y\r
# CONFIG_KVM_SW_PROTECTED_VM is not set\r
# CONFIG_KVM_INTEL_PROVE_VE is not set\r
# CONFIG_KVM_PROVE_MMU is not set\r
# CONFIG_SLUB_TINY is not set\r
# CONFIG_GCC_PLUGIN_CYC_COMPLEXITY is not set\r
# CONFIG_CFG80211_CERTIFICATION_ONUS is not set\r
# CONFIG_PCIE_BUS_TUNE_OFF is not set\r
CONFIG_PCIE_BUS_DEFAULT=y\r
# CONFIG_PCIE_BUS_SAFE is not set\r
# CONFIG_PCIE_BUS_PERFORMANCE is not set\r
# CONFIG_PCIE_BUS_PEER2PEER is not set\r
# CONFIG_PATA_PLATFORM is not set\r
# CONFIG_TTY_PRINTK is not set\r
# CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER is not set\r
# CONFIG_GPIO_SYSFS is not set\r
# CONFIG_VIDEO_TDA1997X is not set\r
# CONFIG_VIDEO_TLV320AIC23B is not set\r
# CONFIG_VIDEO_ADV7180 is not set\r
# CONFIG_VIDEO_ADV7183 is not set\r
# CONFIG_VIDEO_ADV7604 is not set\r
# CONFIG_VIDEO_ADV7842 is not set\r
# CONFIG_VIDEO_BT819 is not set\r
# CONFIG_VIDEO_BT856 is not set\r
# CONFIG_VIDEO_BT866 is not set\r
# CONFIG_VIDEO_KS0127 is not set\r
# CONFIG_VIDEO_ML86V7667 is not set\r
# CONFIG_VIDEO_SAA7110 is not set\r
# CONFIG_VIDEO_TC358743 is not set\r
# CONFIG_VIDEO_TC358746 is not set\r
# CONFIG_VIDEO_TVP514X is not set\r
# CONFIG_VIDEO_TVP7002 is not set\r
# CONFIG_VIDEO_TW9900 is not set\r
# CONFIG_VIDEO_TW9910 is not set\r
# CONFIG_VIDEO_VPX3220 is not set\r
# CONFIG_VIDEO_SAA7185 is not set\r
# CONFIG_VIDEO_ADV7170 is not set\r
# CONFIG_VIDEO_ADV7175 is not set\r
# CONFIG_VIDEO_ADV7343 is not set\r
# CONFIG_VIDEO_ADV7393 is not set\r
# CONFIG_VIDEO_ADV7511 is not set\r
# CONFIG_VIDEO_AD9389B is not set\r
# CONFIG_VIDEO_AK881X is not set\r
# CONFIG_VIDEO_THS8200 is not set\r
# CONFIG_VIDEO_THS7303 is not set\r
# CONFIG_VIDEO_I2C is not set\r
# CONFIG_VIDEO_ST_MIPID02 is not set\r
# CONFIG_VIDEO_GS1662 is not set\r
# CONFIG_MEDIA_TUNER_MSI001 is not set\r
# CONFIG_DVB_S5H1432 is not set\r
# CONFIG_DVB_DIB9000 is not set\r
# CONFIG_DVB_CXD2880 is not set\r
# CONFIG_DVB_MN88443X is not set\r
# CONFIG_DVB_LNBH29 is not set\r
# CONFIG_DVB_LGS8GL5 is not set\r
# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set\r
# CONFIG_DRM_DEBUG_MODESET_LOCK is not set\r
# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set\r
# CONFIG_DRM_I915_WERROR is not set\r
# CONFIG_DRM_I915_DEBUG is not set\r
# CONFIG_DRM_I915_DEBUG_MMIO is not set\r
# CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS is not set\r
# CONFIG_DRM_I915_SW_FENCE_CHECK_DAG is not set\r
# CONFIG_DRM_I915_DEBUG_GUC is not set\r
# CONFIG_DRM_I915_SELFTEST is not set\r
# CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS is not set\r
# CONFIG_DRM_I915_DEBUG_VBLANK_EVADE is not set\r
# CONFIG_DRM_I915_DEBUG_RUNTIME_PM is not set\r
# CONFIG_DRM_AMDGPU_WERROR is not set\r
# CONFIG_DRM_XE_WERROR is not set\r
# CONFIG_DRM_XE_DEBUG is not set\r
# CONFIG_DRM_XE_DEBUG_VM is not set\r
# CONFIG_DRM_XE_DEBUG_SRIOV is not set\r
# CONFIG_DRM_XE_DEBUG_MEM is not set\r
# CONFIG_DRM_XE_LARGE_GUC_BUFFER is not set\r
# CONFIG_DRM_XE_USERPTR_INVAL_INJECT is not set\r
# CONFIG_DRM_MGAG200_DISABLE_WRITECOMBINE is not set\r
# CONFIG_DRM_WERROR is not set\r
# CONFIG_RESET_SIMPLE is not set\r
# CONFIG_KFENCE_STATIC_KEYS is not set\r
# CONFIG_FB_INTEL is not set\r
# CONFIG_SND_SOC_SOF_DEVELOPER_SUPPORT is not set\r
# CONFIG_USB_KBD is not set\r
# CONFIG_USB_MOUSE is not set\r
# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set\r
# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set\r
CONFIG_PAHOLE_HAS_SPLIT_BTF=y\r
CONFIG_DEBUG_INFO_BTF_MODULES=y\r
# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set\r
# CONFIG_WIRELESS_WDS is not set\r
# CONFIG_UNWINDER_GUESS is not set\r
# CONFIG_TRIM_UNUSED_KSYMS is not set\r
# CONFIG_VMLINUX_MAP is not set\r
# end of config expert\n">> ./.config
fi

  # ntsync support
  tkgpatch="$srcdir/0007-v${_basekernel}-ntsync.patch"
  if [ -e "$tkgpatch" ]; then
    if [ -z "$_ntsync" ]; then
      plain ""
      plain "Enable support for NTsync, an experimental replacement for esync"
      plain "https://repo.or.cz/linux/zf.git/shortlog/refs/heads/ntsync5"
      warning "Alternatively, on Arch you can use the DKMS module which allows for using the feature on multiple kernels side by side: https://aur.archlinux.org/packages/ntsync-dkms"
      read -rp "`echo $'    > N/y : '`" CONDITION_ntsync;
    fi
    if [[ "$CONDITION_ntsync" =~ [yY] ]] || [ "$_ntsync" = "true" ]; then
      _msg="Patching NTsync support"
      _tkg_patcher
      _module "NTSYNC"
      echo "KERNEL==\"ntsync\", MODE=\"0644\"" > "${srcdir}"/ntsync.rules
      echo "ntsync" > "${srcdir}"/ntsync.conf
    fi
  elif (( "$_kver" >= 614 )); then
    # For /etc/modules-load.d/ntsync.conf to autoload ntsync module
    echo "ntsync" > "${srcdir}"/ntsync.conf
  fi

  # OpenRGB support
  tkgpatch="$srcdir/0014-OpenRGB.patch"
  if [ -e "$tkgpatch" ]; then
    if [ "$_openrgb" = "true" ]; then
      _msg="Import OpenRGB patch" && _tkg_patcher
    fi
  fi

  # We're done with tkgpatch
  unset tkgpatch
  unset _msg

  # Waydroid - mostly for other distros as those are enabled on Arch
  # PSI disabled on PDS/BMQ, so omit those
  if [ "${_cpusched}" != "pds" ] && [ "${_cpusched}" != "bmq" ]; then
    _enable "ANDROID" "ANDROID_BINDER_IPC" "ANDROID_BINDERFS"
    _disable "ANDROID_BINDER_IPC_SELFTEST"
    scripts/config --set-str "ANDROID_BINDER_DEVICES" ""
  fi

  # NR_CPUS
  if [ "$_basever" != "601" ]; then
    if [ -n "$_NR_CPUS_value" ]; then
      msg2 "Setting Max core number that kernel can handle to $_NR_CPUS_value"
      _disable CPUMASK_OFFSTACK MAXSMP
      scripts/config --set-val "NR_CPUS" "$_NR_CPUS_value"
    fi
  else
    warning "NR_CPUS is bugged on 6.1.y, so your setting was ignored"
  fi

  fi

  # Community patches
  if [ -n "$_community_patches" ]; then
    if [ ! -d "$_where/../community-patches" ]; then
      cd "$_where/.." && git clone https://github.com/Frogging-Family/community-patches.git && cd "${_kernel_work_folder_abs}"
    fi
    _community_patches=($_community_patches)
    mkdir -p "$_where"/linux"$_basever"-tkg-userpatches
    for _p in ${_community_patches[@]}; do
      if [ ! -e "$_where/linux$_basever-tkg-userpatches/$_p" ]; then
        ln -s "$_where"/../community-patches/linux"$_basever"-tkg/$_p "$_where"/linux"$_basever"-tkg-userpatches/$_p
      else
        warning "Ignoring '$_p' community patch already present in the userpatches dir"
        _community_patches=(${_community_patches[@]/$_p})
      fi
    done
  fi

  # userpatches
  if [ "$_user_patches" = "true" ]; then
    _userpatch_target="linux-${_basekernel}"
    _userpatch_ext="my"
    user_patcher
  fi

  # Community patches removal
  for _p in ${_community_patches[@]}; do
    rm -f "$_where"/linux"$_basever"-tkg-userpatches/$_p
  done

  if [ "$_distro" = "Arch" ]; then
    # don't run depmod on 'make install'. We'll do this ourselves in packaging
    sed -i '2iexit 0' scripts/depmod.sh

    # get kernel version
    #make prepare ${llvm_opt}
  fi

  # modprobed-db

  if [[ "$_modprobeddb" = "true" && "$_kernel_on_diet" == "true" ]]; then
    msg2 "_modprobeddb and _kernel_on_diet cannot be used together: it doesn't make sense, _kernel_on_diet uses our own modprobed list ;)"
    exit 1
  fi

  if [[ "$_kernel_on_diet" == "true" && "$_kver" -lt 605 ]]; then
    msg2 "_kernel_on_diet not implemented for kernels older than 6.5"
    exit 1
  fi

  if [[ "$_modprobeddb" = "true" || "$_kernel_on_diet" == "true" ]]; then
    msg2 "Using modprobed-db"
    if [ -f "$_where"/"$_modprobeddb_db_path" ]; then
      _modprobeddb_db_path="$_where"/"$_modprobeddb_db_path"
    fi
    if [ "$_kernel_on_diet" == "true" ]; then
      msg2 "Using linux-tkg diet db"
      _modprobeddb_db_path="$_where/minimal-modprobed.db"
    fi
    if [ ! -f $_modprobeddb_db_path ]; then
      msg2 "modprobed-db database not found"
      exit 1
    fi

    yes "" | make LSMOD=$_modprobeddb_db_path localmodconfig ${llvm_opt}
  fi

  if [ true = "$_config_fragments" ]; then
    local fragments=()
    mapfile -d '' -t fragments < <(find "$_where"/ -type f -name "*.myfrag" -print0 | sort -z)

    if [ true = "$_config_fragments_no_confirm" ]; then
      printf 'Using config fragment %s\n' "${fragments[@]#$_where/}" #"
    else
      for i in "${!fragments[@]}"; do
        while true; do
          read -r -p 'Found config fragment '"${fragments[$i]#$_where/}"', apply it? [y/N] ' CONDITIONMPDB #"
          CONDITIONMPDB="$(printf '%s' "$CONDITIONMPDB" | tr '[:upper:]' '[:lower:]')"
          case "$CONDITIONMPDB" in
            y|yes)
              break;;
            n|no|'')
              unset fragments[$i]
              break;;
            *)
              echo 'Please answer with yes or no'
          esac
        done
      done
    fi

    if [ 0 -lt "${#fragments[@]}" ]; then
      scripts/kconfig/merge_config.sh -m .config "${fragments[@]}"
    fi
  fi

  # rewrite configuration
  msg2 "Setting config"
  make ${_config_updating} ${llvm_opt} |& tee -a "$_where"/logs/prepare.log.txt

  # Modify the kernel config file to fit Fedora SELinux configuration
  if [ "$_distro" = "Fedora" ] ; then
    msg2 "SELinux activation for Fedora"
    _enable "AUDIT"
    _enable "SECURITY_SELINUX"
    _enable "DEFAULT_SECURITY_SELINUX"
    _disable "DEFAULT_SECURITY_DAC"
    scripts/config --set-str "LSM" "lockdown,yama,integrity,selinux,bpf,landlock"
  fi

  # menuconfig / nconfig
  if [ -z "$_menunconfig" ]; then
    plain ""
    plain "*Optional* For advanced users - Do you want to use make menuconfig or nconfig"
    plain "to configure the kernel before building it?"
    plain "If you do, make sure your terminal is currently"
    plain "at least 19 lines by 80 columns large or you'll get an error :D"
    read -rp "`echo $'    > 0. nope\n      1. menuconfig\n      2. nconfig\n      3. xconfig\n      choice[0-3?]: '`" CONDITIONMNC;
    _menunconfig="$CONDITIONMNC"
  fi
  if [ 1 = "$_menunconfig" ]; then
    cp .config .config.orig
    make menuconfig ${llvm_opt}
  elif [ 2 = "$_menunconfig" ]; then
    cp .config .config.orig
    make nconfig ${llvm_opt}
  elif [ 3 = "$_menunconfig" ]; then
    cp .config .config.orig
    make xconfig ${llvm_opt}
  fi
  if [ 1 = "$_menunconfig" ] || [ 2 = "$_menunconfig" ] || [ 3 = "$_menunconfig" ]; then
    if [ -z "${_diffconfig}" ]; then
      while true; do
        read -r -p 'Generate a config fragment from your changes? [y/N] ' CONDITIONF
        CONDITIONF="$(printf '%s' "$CONDITIONF" | tr '[:upper:]' '[:lower:]')"
        case "$CONDITIONF" in
          y|yes)
            _diffconfig=true
            break;;
          n|no|'')
            _diffconfig=false
            break;;
          *)
            echo 'Please answer with yes or no'
        esac
      done
    fi
    if [ true = "$_diffconfig" ]; then
      if [ -z "$_diffconfig_name" ]; then
        IFS= read -r -p 'Filename for the config fragment [leave empty to not generate fragment]: ' _diffconfig_name
      fi
      if [ -z "$_diffconfig_name" ]; then
        echo 'No file name given, not generating config fragment.'
      else
        pushd "$_kernel_work_folder_abs"
        scripts/diffconfig -m .config.orig .config > "$_where/$_diffconfig_name"
        popd
      fi
    fi
    rm .config.orig
  fi

  if [ "$_distro" = "Arch" ]; then
    make -s kernelrelease > version
    msg2 "Prepared %s version %s" "$pkgbase" "$(<version)"
  fi

  # Hardcode the -march and -mtune in the Makefile so it persists for DKMS building
  # See https://github.com/Frogging-Family/linux-tkg/issues/1099

  # 1. Get the line number where the user KCFLAGS, KCPPFLAGS and KRUSTFLAGS env vars are taken into account
  # 2. Insert the -march and -mtune flags just before so it sets a default that can still be overriden by those variables, if set by the user

  local _mtune="$_processor_opt"
  [[ "$_processor_opt" =~ x86-64 ]] && _mtune="generic"

  set -o pipefail
  if local cflags_source_line_number=$(grep -F -n 'KBUILD_CFLAGS   += $(KCFLAGS)' Makefile 2> /dev/null | cut -d":" -f1); then
    sed -i "${cflags_source_line_number}i KBUILD_CFLAGS   += -mtune=$_mtune" Makefile
    sed -i "${cflags_source_line_number}i KBUILD_CFLAGS   += -march=$_processor_opt" Makefile
    sed -i "${cflags_source_line_number}i # The following two lines are added by linux-tkg script" Makefile
  else
    warning "Couldn't set -march and -mtune for C files in Makefile. Please open a bug report on this"
    [[ "$_nofallback" = "true" ]] && exit 1
  fi

  if local cppflags_source_line_number=$(grep -F -n 'KBUILD_CPPFLAGS += $(KCPPFLAGS)' Makefile 2> /dev/null | cut -d":" -f1); then
    sed -i "${cppflags_source_line_number}i KBUILD_CPPFLAGS += -mtune=$_mtune" Makefile
    sed -i "${cppflags_source_line_number}i KBUILD_CPPFLAGS += -march=$_processor_opt" Makefile
    sed -i "${cppflags_source_line_number}i # The following two lines are added by linux-tkg script" Makefile
  else
    warning "Couldn't set -march and -mtune for C++ files in Makefile. Please open a bug report on this"
    [[ "$_nofallback" = "true" ]] && exit 1
  fi

  # if local rustflags_source_line_number=$(grep -F -n 'KBUILD_RUSTFLAGS += $(KRUSTFLAGS)' Makefile 2> /dev/null | cut -d":" -f1); then
  #   sed -i "${rustflags_source_line_number}i KBUILD_RUSTFLAGS += -Ztune-cpu=$_mtune" Makefile
  #   sed -i "${rustflags_source_line_number}i KBUILD_RUSTFLAGS += -Ctarget-cpu=$_processor_opt" Makefile
  #   sed -i "${rustflags_source_line_number}i # The following two lines are added by linux-tkg script" Makefile
  # else
  #   warning "Couldn't set -march and -mtune for Rust files in Makefile. Please open a bug report on this"
  #   [[ "$_nofallback" = "true" ]] && exit 1
  # fi
  set +o pipefail

  # copy new config file back to the user's git folder for an eventual future use
  cp .config "${_where}"/kernelconfig.new
}

exit_cleanup() {

  # Move eventual shell-output.log file to logs folder
  if [ -f "$_where/shell-output.log" ]; then
    mv -f "$_where"/shell-output.log "$_where"/logs/shell-output.log.txt
    sed -i 's/\x1b\[[0-9;]*m//g' "$_where"/logs/shell-output.log.txt
    sed -i 's/\x1b(B//g' "$_where"/logs/shell-output.log.txt
  fi

  # Remove temporarily copied files
  rm -rf "$_where"/*.patch
  rm -rf "$_where"/*-profile.cfg
  rm -f "$_where"/config*
  rm -f "$_where"/*.hook
  rm -f "$_where"/cleanup
  rm -f "$_where"/prepare

  # Remove state tracker
  rm -f "$_where"/BIG_UGLY_FROGMINER

  # Remove ntsync rules file
  rm -f "$_where"/ntsync.rules

  # Community patches removal in case of failure
  for _p in ${_community_patches[@]}; do
    rm -f "$_where"/linux"$_basever"-tkg-userpatches/"$_p"
  done

  if [ "$_NUKR" = "true" ] && [ "$_where" != "$srcdir" ]; then
    rm -rf "$_where"/src/*
    # Double tap
    rm -rf "$srcdir"/linux-*
    rm -rf "$srcdir"/*.xz
    rm -rf "$srcdir"/*.patch
    rm -rf "$srcdir"/*-profile.cfg
    rm -rf "$srcdir"/*.rules
    rm -f "$srcdir"/config.x86_64
    rm -f "$srcdir"/customization.cfg

  elif [ "$_distro" == "Arch" ]; then
    rm -rf "$srcdir"/linux-${_basekernel}/Documentation/filesystems/aufs/*
    rm -f "$srcdir"/linux-${_basekernel}/Documentation/ABI/testing/*-aufs
    rm -rf "$srcdir"/linux-${_basekernel}/fs/aufs/*
    rm -f "$srcdir"/linux-${_basekernel}/include/uapi/linux/aufs*

    rm -f "$srcdir"/linux-${_basekernel}/mm/prfile.c

    rm -f "$srcdir"/linux-${_basekernel}/block/bfq*

    rm -rf "$srcdir"/linux-${_basekernel}/drivers/scsi/vhba/*

    rm -rf "$srcdir"/linux-${_basekernel}/fs/exfat/*
    rm -f "$srcdir"/linux-${_basekernel}/include/trace/events/fs.h

    rm -f "$srcdir"/linux-${_basekernel}/Documentation/scheduler/sched-PDS-mq.txt
    rm -f "$srcdir"/linux-${_basekernel}/include/linux/skip_list.h
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/pds.c
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/pds_sched.h

    rm -f "$srcdir"/linux-${_basekernel}/Documentation/scheduler/sched-BMQ.txt
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/${_sched}.c
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/sched/alt_debug.c
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/alt_sched.h

    rm -f "$srcdir"/linux-${_basekernel}/Documentation/scheduler/sched-BFS.txt
    rm -f "$srcdir"/linux-${_basekernel}/Documentation/scheduler/sched-MuQSS.txt
    rm -rf "$srcdir"/linux-${_basekernel}/arch/blackfin/*
    rm -f "$srcdir"/linux-${_basekernel}/arch/powerpc/configs/c2k_defconfig
    rm -f "$srcdir"/linux-${_basekernel}/arch/score/configs/spct6600_defconfig
    rm -f "$srcdir"/linux-${_basekernel}/arch/tile/configs/tilegx_defconfig
    rm -f "$srcdir"/linux-${_basekernel}/arch/tile/configs/tilepro_defconfig
    rm -f "$srcdir"/linux-${_basekernel}/drivers/staging/lustre/lnet/lnet/lib-eq.c
    rm -f "$srcdir"/linux-${_basekernel}/kernel/sched/MuQSS*
    rm -f "$srcdir"/linux-${_basekernel}/kernel/skip_list.c

    rm -f "$srcdir"/linux-${_basekernel}/Documentation/vm/uksm.txt
    rm -f "$srcdir"/linux-${_basekernel}/include/linux/sradix-tree.h
    rm -f "$srcdir"/linux-${_basekernel}/include/linux/uksm.h
    rm -f "$srcdir"/linux-${_basekernel}/lib/sradix-tree.c
    rm -f "$srcdir"/linux-${_basekernel}/mm/uksm.c
  fi

  if [ "${_distro}" = "Arch" ]; then
    remove_deps || ( true && msg2 "remove_deps not found" )
  fi

  msg2 'exit cleanup done\n'
  if [ -n "$_runtime" ]; then
    msg2 "compilation time : \n$_runtime"
  fi

  # Copy over the customization.cfg file to the logs folder
  cp -f "$_where"/customization.cfg "$_where"/logs/customization.cfg.txt

  [[ -f "$_where"/git_work.log.txt ]] && \
    mv -f "$_where"/git_work.log.txt "$_where"/logs/git_work.log.txt

  # Create logs/system-info.txt
  cat /etc/os-release > "$_where"/logs/system-info.txt
  if command -v clang &> /dev/null; then
    echo "#################" >> "$_where"/logs/system-info.txt
    clang -v >> "$_where"/logs/system-info.txt 2>&1
  fi
  echo "#################" >> "$_where"/logs/system-info.txt
  gcc -v >> "$_where"/logs/system-info.txt 2>&1

  # Arch: move shell-output.log to logs folder
  if [[ "$_distro" = "Arch" && -f "$_where"/shell-output.log ]]; then
    mv -f "$_where"/shell-output.log "$_where"/logs/shell-output.log.txt
    sed -i 's/\x1b\[[0-9;]*m//g' "$_where"/logs/shell-output.log.txt
    sed -i 's/\x1b(B//g' "$_where"/logs/shell-output.log.txt
  fi
}

trap exit_cleanup EXIT
